﻿using System;
using System.Globalization;
using System.Linq;

namespace Common
{
    /// <summary>
    /// 字符串扩展方法类
    /// </summary>
    public static class StringExtension
    {
        #region 解析字符串为int、decimal、DateTime、Enum
        /// <summary>
        /// 解析字符串为Int32?
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static int? ToInt32(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return null;

            int v;
            if (int.TryParse(t.Trim(), out v))
                return v;
            return null;
        }

        /// <summary>
        /// 解析字符串为Int32，如无法解析抛出异常
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static int ToInt32Req(this string t)
        {
            int? i = t.ToInt32();
            if (!i.HasValue)
                throw new ArgumentException(string.Format("无法将值“{0}”解析为Int32类型。", t));
            return i.Value;
        }

        /// <summary>
        /// 解析字符串为Int32
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static int ToInt32OrDefault(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return default(int);

            int v;
            if (int.TryParse(t.Trim(), out v))
                return v;
            return default(int);
        }

        /// <summary>
        /// 解析字符串为Int32
        /// </summary>
        /// <param name="t"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static int ToInt32OrDefault(this string t, int defaultValue)
        {
            if (string.IsNullOrEmpty(t))
                return defaultValue;

            int v;
            if (int.TryParse(t.Trim(), out v))
                return v;
            return defaultValue;
        }

        /// <summary>
        /// 解析字符串为Int64?
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static long? ToInt64(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return null;

            long v;
            if (long.TryParse(t.Trim(), out v))
                return v;
            return null;
        }

        /// <summary>
        /// 解析字符串为Int64，如无法解析抛出异常
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static long ToInt64Req(this string t)
        {
            long? l = t.ToInt64();
            if (!l.HasValue)
                throw new ArgumentException(string.Format("无法将值“{0}”解析为Int64类型。", t));
            return l.Value;
        }

        /// <summary>
        /// 解析字符串为Int64
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static long ToInt64OrDefault(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return default(long);

            long v;
            if (long.TryParse(t.Trim(), out v))
                return v;
            return default(long);
        }

        /// <summary>
        /// 解析字符串为Int64
        /// </summary>
        /// <param name="t"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static long ToInt64OrDefault(this string t, long defaultValue)
        {
            if (string.IsNullOrEmpty(t))
                return defaultValue;

            long v;
            if (long.TryParse(t.Trim(), out v))
                return v;
            return defaultValue;
        }

        /// <summary>
        /// 解析字符串为decimal?
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static decimal? ToDecimal(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return null;

            decimal v;
            if (decimal.TryParse(t.Trim(), out v))
                return v;
            return null;
        }

        /// <summary>
        /// 解析字符串为decimal，如无法解析抛出异常
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static decimal ToDecimalReq(this string t)
        {
            decimal? d = t.ToDecimal();
            if (!d.HasValue)
                throw new ArgumentException(string.Format("无法将值“{0}”解析为decimal类型。", t));
            return d.Value;
        }

        /// <summary>
        /// 解析字符串为decimal
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static decimal ToDecimalOrDefault(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return default(decimal);

            decimal v;
            if (decimal.TryParse(t.Trim(), out v))
                return v;
            return default(decimal);
        }

        /// <summary>
        /// 解析字符串为decimal
        /// </summary>
        /// <param name="t"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static decimal ToDecimalOrDefault(this string t, decimal defaultValue)
        {
            if (string.IsNullOrEmpty(t))
                return defaultValue;

            decimal v;
            if (decimal.TryParse(t.Trim(), out v))
                return v;
            return defaultValue;
        }

        /// <summary>
        /// 解析字符串为double?
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static double? ToDouble(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return null;

            double v;
            if (double.TryParse(t.Trim(), out v))
                return v;
            return null;
        }

        /// <summary>
        /// 解析字符串为double，如无法解析抛出异常
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static double ToDoubleReq(this string t)
        {
            double? d = t.ToDouble();
            if (!d.HasValue)
                throw new ArgumentException(string.Format("无法将值“{0}”解析为double类型。", t));
            return d.Value;
        }

        /// <summary>
        /// 解析字符串为double
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static double ToDoubleOrDefault(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return default(double);

            double v;
            if (double.TryParse(t.Trim(), out v))
                return v;
            return default(double);
        }

        /// <summary>
        /// 解析字符串为double
        /// </summary>
        /// <param name="t"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static double ToDoubleOrDefault(this string t, double defaultValue)
        {
            if (string.IsNullOrEmpty(t))
                return defaultValue;

            double v;
            if (double.TryParse(t.Trim(), out v))
                return v;
            return defaultValue;
        }

        /// <summary>
        /// 解析字符串为float?
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static float? ToFloat(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return null;

            float v;
            if (float.TryParse(t.Trim(), out v))
                return v;
            return null;
        }

        /// <summary>
        /// 解析字符串为float，如无法解析抛出异常
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static float ToFloatReq(this string t)
        {
            float? f = t.ToFloat();
            if (!f.HasValue)
                throw new ArgumentException(string.Format("无法将值“{0}”解析为float类型。", t));
            return f.Value;
        }

        /// <summary>
        /// 解析字符串为float
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static float ToFloatOrDefault(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return default(float);

            float v;
            if (float.TryParse(t.Trim(), out v))
                return v;
            return default(float);
        }

        /// <summary>
        /// 解析字符串为float
        /// </summary>
        /// <param name="t"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static float ToFloatOrDefault(this string t, float defaultValue)
        {
            if (string.IsNullOrEmpty(t))
                return defaultValue;

            float v;
            if (float.TryParse(t.Trim(), out v))
                return v;
            return defaultValue;
        }

        //---------------------------------------------------------------------------

        /// <summary>
        /// 解析字符串为bool?
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static bool? ToBool(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return null;

            bool v;
            if (bool.TryParse(t.Trim(), out v))
                return v;
            return null;
        }

        /// <summary>
        /// 解析字符串为bool，如无法解析抛出异常
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static bool ToBoolReq(this string t)
        {
            bool? b = t.ToBool();
            if (!b.HasValue)
                throw new ArgumentException(string.Format("无法将值“{0}”解析为bool类型。", t));
            return b.Value;
        }

        /// <summary>
        /// 解析字符串为bool
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static bool ToBoolOrDefault(this string t)
        {
            if (string.IsNullOrEmpty(t))
                return default(bool);

            bool v;
            if (bool.TryParse(t.Trim(), out v))
                return v;
            return default(bool);
        }

        /// <summary>
        /// 解析字符串为bool
        /// </summary>
        /// <param name="t"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static bool ToBoolOrDefault(this string t, bool defaultValue)
        {
            if (string.IsNullOrEmpty(t))
                return defaultValue;

            bool v;
            if (bool.TryParse(t.Trim(), out v))
                return v;
            return defaultValue;
        }

        /// <summary>
        /// 字符串转为DateTime类型
        /// </summary>
        /// <param name="str"></param>
        /// <param name="dateStyle"></param>
        /// <returns></returns>
        public static DateTime ToDateTime(this string str, string dateStyle = "")
        {
            string[] formats = { "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd", "yyyyMMddHHmmss", "yyyyMMdd" };
            DateTime dateTime = new DateTime();

            if (!string.IsNullOrEmpty(dateStyle))
            {
                DateTime.TryParseExact(str, dateStyle, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime);
            }
            else
            {
                DateTime.TryParseExact(str, formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime);
            }

            return dateTime;
        }

        /// <summary>
        /// 字符串转为DateTime?类型
        /// </summary>
        /// <param name="str"></param>
        /// <param name="dateStyle"></param>
        /// <returns></returns>
        public static DateTime? ToDateTimeCanNull(this string str, string dateStyle = "")
        {
            string[] formats = { "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd", "yyyyMMddHHmmss", "yyyyMMdd" };
            DateTime dateTime;

            if (!string.IsNullOrEmpty(dateStyle))
            {
                if (DateTime.TryParseExact(str, dateStyle, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
                {
                    return dateTime;
                }
                else
                {
                    return null;
                }
            }
            else
            {
                if (DateTime.TryParseExact(str, formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
                {
                    return dateTime;
                }
                else
                {
                    return null;
                }
            }
        }



        //---------------------------------------------------------------------------

        #region 枚举（使用这个帮助类，主要是因为这个，用起来方便，推荐使用）


        /// <summary>
        /// 解析字符串为Enum，与Enum.Parse不同，不在枚举类定义的值无法解析
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T? ToEnum<T>(this string t) where T : struct
        {
            if (!typeof(T).IsEnum)
                throw new NotSupportedException(string.Format("{0}必须为枚举类型。", typeof(T)));

            //使用Enum.Parse时，如果解析一个未在枚举中定义的值，如8这个值
            //这时虽然Enum.IsDefined(type, 8)和Enum.IsDefined(type, "8")都返回false
            //但是使用Enum.Parse却可以解析出值8，用IsDefined可以限定值有效

            if (string.IsNullOrEmpty(t))
                return null;

            //if (Enum.IsDefined(typeof(T), t))
            //    return (T)Enum.Parse(typeof(T), t);
            //return null;

            //try { return (T)Enum.Parse(typeof(T), t); }
            //catch { }

            foreach (T value in Enum.GetValues(typeof(T)))
            {
                //Name相等或值相等
                if (value.ToString() == t.Trim()
                    || typeof(T).GetField(value.ToString()).GetRawConstantValue().ToString() == t.Trim())
                    return value;
            }
            return null;
        }

        /// <summary>
        /// 解析字符串为Enum，与Enum.Parse不同，未在枚举类定义的值无法解析，如无法解析抛出异常
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static T ToEnumReq<T>(this string t) where T : struct
        {
            T? e = t.ToEnum<T>();
            if (!e.HasValue)
                throw new ArgumentException(string.Format("无法将值“{0}”解析为“{1}”枚举类型。", t, typeof(T)));
            return e.Value;
        }

        /// <summary>
        /// 解析字符串为Enum，与Enum.Parse不同，未在枚举类定义的值无法解析
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T ToEnumOrDefault<T>(this string t) where T : struct
        {
            return t.ToEnumOrDefault(default(T));
        }

        /// <summary>
        /// 解析字符串为Enum，与Enum.Parse不同，未在枚举类定义的值无法解析
        /// </summary>
        /// <param name="t"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static T ToEnumOrDefault<T>(this string t, T defaultValue) where T : struct
        {
            T? e = t.ToEnum<T>();

            if (e.HasValue)
                return e.Value;

            return defaultValue;

            //foreach (T value in Enum.GetValues(typeof(T)))
            //{
            //    if (value.ToString() == t
            //        || typeof(T).GetField(value.ToString()).GetRawConstantValue().ToString() == t)
            //        return value;
            //}
            //return defaultValue;
        }

        ///// <summary>
        ///// 取枚举名称
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <param name="t"></param>
        ///// <returns></returns>
        //public static string ToEnumName<T>(this string t) where T : struct
        //{
        //    T? e = t.ToEnum<T>();

        //    if (e.HasValue)
        //        return e.Value.ToString();

        //    return string.Empty;
        //}

        /// <summary>
        /// 根据枚举Text解析为Enum（Text可能重复，此方法返回第一个找到的值）
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T? ToEnumByText<T>(this string t) where T : struct
        {
            if (!typeof(T).IsEnum)
                throw new NotSupportedException(string.Format("{0}必须为枚举类型。", typeof(T)));

            //if (string.IsNullOrEmpty(t))
            //	return null;

            foreach (T value in Enum.GetValues(typeof(T)))
            {
                var attributes = (EnumTextAttribute[])typeof(T).GetField(value.ToString()).GetCustomAttributes(typeof(EnumTextAttribute), false);
                string text = value.ToString();
                if (attributes.Count() > 0)
                    text = attributes.First().Value;

                if (text == t)
                    return value;
            }
            return null;
        }

        /// <summary>
        /// 根据枚举Text解析为Enum，如找不到抛出异常（Text可能重复，此方法返回第一个找到的值）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static T ToEnumReqByText<T>(this string t) where T : struct
        {
            T? e = t.ToEnumByText<T>();
            if (!e.HasValue)
                throw new ArgumentException(string.Format("无法找到枚举类型“{1}”文本为“{0}”的值。", t, typeof(T)));
            return e.Value;
        }

        ///// <summary>
        ///// 根据枚举Text解析为Enum
        ///// </summary>
        ///// <param name="t"></param>
        ///// <returns></returns>
        //public static T ToEnumOrDefaultByText<T>(this string t) where T : struct
        //{
        //	return t.ToEnumOrDefault(default(T));
        //}

        /// <summary>
        /// 根据枚举Text解析为Enum（Text可能重复，此方法返回第一个找到的值）
        /// </summary>
        /// <param name="t"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static T ToEnumOrDefaultByText<T>(this string t, T defaultValue = default(T)) where T : struct
        {
            T? e = t.ToEnumByText<T>();

            if (e.HasValue)
                return e.Value;

            return defaultValue;
        }


        #endregion


        #endregion
    }
}
